/*****************
 * init_api()
 * funkce pro inicializaci prace s API funkcemi
 */
int init_api(void) {
  SetInterrupt(OSINT ,(unsigned int)_int);
  return 0;
}

/**
 *  poke()
 *  na adresu <cis>:[<sic>] preda hodnotu <val>
 */
void poke(unsigned int cis, unsigned int sic, int val) {
  asm mov si, sic       ;
  asm mov es, cis       ;
  asm mov ax, val       ;
  asm mov es:[si], ax   ;
  return;
}

/**
 *  SetInterrupt()
 *  nastavi dane cislo interruptu dane funkce
 */
void SetInterrupt(unsigned char IntNo, unsigned int handler) {
  asm cli;
  poke(0x0000, (IntNo << 2),   handler);
  poke(0x0000, (IntNo << 2)+2, _CS);
  asm sti;
  return;
}

/**
 *  APIHandler()
 *  funkce, ktera podle cisla v AX provede
 *  prislusnou operaci
 */
void APIHandler(_regs *r) {
/* deklarace promennych, pouzitych jako navratove hodnoty */
char      c_ret_val    = 0;                /* navratova hodnota pro  [char]   */
int       i_ret_val    = 0;                /* navratova hodnota pro  [int]    */
long      l_ret_val    = 0;                /* navratova hodnota pro  [long]   */
bool      bool_ret_val = false;            /* navratova promenna pro [bool]   */
void     *ptr_ret_val  = (void *)NULL;     /* navratova promenna pro pointery */

/* potencialni fyzicka adresa volane funkce */
unsigned long r_ebx = ((unsigned long)r->segment + (unsigned int)r->bx);

/* pointer na seznam vstupnich hodnot */
va_list   arg, mod_arg;

/* promenne, ktere budou pouzity jako vstupni promenne volanych API funkci */
int       i1, i2, i3, i4;
void      *p1, *p2, *p3, *p4;
char      ch1, ch2, ch3, ch4;
bool      b1, b2, b3, b4;
void far *fp1, far *fp2, far *fp3, far *fp4;
char far *cp1;

/* adresa pro "nic", zde se odlozi prvni "vstupni hodnota" funkce,
   ktera je ve skutecnosti jen adresa volane funkce */
void far *nil;

/******************************************************************************/

  if(r_ebx) {
    arg = (void far *)r_ebx;
    nil = va_arg(arg, void far *);
  }

  /* dosadime globalni promenne */
  _api_lsegment     = r->segment;
  _actual_segment   = _api_segment;
  _actual_lsegment  = _api_lsegment;

  /* pricteme counter API */
  _api_counter++;

/***************************************************\
 *            HLAVNI OBSLUHA API                   *
 *  princip API:                                   *
 *                                                 *
 *  /* 0x20   ->  findfile()  [find_file()] *\     *           //{cislo API} ->
 *                                                 *             {nazev funkce (app)}
 *                                                 *             {[nazev funkce (kernel)]}
 *                                                 *
 * if(r->ax == 0x20) {                             *           //porovnani
 *                                                 *
 *   fp1 = (void far *)                            *           //<fp1> je far ukazatel,
 *                                                 *             proto ho taky tak musime prevest
 *                                                 *            (void far *)
 *                                                 *
 *   (_api_lsegment +                              *           //@_api_lsegment je cislo odpovidajici
 *                                                 *             implicitne 0x20000000,
 *                                                 *             coz je 2000h segment,
 *                                                 *             ve kterem bezi spustene aplikace,
 *                                                 *             je nutne toto cislo pricist z duvodu
 *                                                 *             vytvoreni plne adresy k vytvoreni
 *                                                 *             pointeru
 *                                                 *
 *   (int)(                                        *           //k far pointeru lze
 *                                                 *             pricitat pouze [int]
 *                                                 *
 *    (DirectoryEntry *)va_arg(arg, DirectoryEntry *))         //vvtupni hodnota je vsak pointer
 *                                                 *             na strukturu DirectoryEntry
 *   );                                            *
 *                                                 *
 *   i1 = (int)va_arg(arg, int);                   *           //dalsi vstupni promenna je [int],
 *                                                 *             va_arg() bere vstupni paramentry z fce
 *                                                 *
 *    i_ret_val = api_findfile((DirectoryEntry far *)fp1, i1); //zavolame nasi funkcni findfile()
 *                                                 *             (musime mit vytvorenou i druhou fci
 *                                                 *              pro API, tedy api_findfile()),
 *                                                 *              vstupni hodnoty jsou prevedeny
 *                                                 *              na typy, ktere funkce prijima
 *                                                 *              a vystupni hodnota ([int])
 *                                                 *              je asociovana s promennou i_ret_val;
 *                                                 *
 *   arg = (void far *)(r_ebx - 2*sizeof(int));    *           //nastaveni pointeru <arg> na adresu,
 *                                                 *            kde se nachazi vystupni hodnota
 *                                                 *
 *   (int)(va_arg(arg, int)) = i_ret_val;          *           //asociovani navratove hodnoty
 * }                                               *
\***************************************************/

/****************************************************
 *  DRIVERS                                       ***
 ***************************************************/
  /* 0x00   ->  getch()           [getch()] */
  if(r->ax == 0x00) {
    arg = (void far *)(r_ebx - 2*sizeof(int));
    (int)(va_arg(arg, int)) = getch();
  }

  /* 0x01   ->  putch()           [putch()] */
  if(r->ax == 0x01) {
    ch1 = TEXT_COL;
    textcolor((r->bx >> 8) & 0xFF);
    putch(r->bx & 0xFF);
    textcolor(ch1);
  }

/****************************************************
 *  MEMORY MANAGEMENT                             ***
 ***************************************************/
  /* 0x10   ->  malloc()          [mem_alloc()] */
  if(r->ax == 0x10) {
    i1 = (int)va_arg(arg, int);

    ptr_ret_val = (void *)mem_alloc(i1, _api_segment);

    arg = (void far *)(r_ebx - 2*sizeof(void *));
    (void *)(va_arg(arg, void *)) = ptr_ret_val;
  }

  /* 0x11   ->  free()            [mem_free()] */
  if(r->ax == 0x11) {
    fp1 = (char far *)(_api_lsegment + (unsigned int)va_arg(arg, void *));
    mem_free(fp1);
  }

  /* 0x12   ->  realloc()         [mem_realloc()] */
  if(r->ax == 0x12) {
    fp1 = (char far *)(_api_lsegment + (unsigned int)va_arg(arg, void *));
    i1 = (int)va_arg(arg, int);

    ptr_ret_val = (void *)mem_realloc(fp1, i1);

    arg=(void far *)(r_ebx - 2*sizeof(void *));
    (void *)(va_arg(arg, void *)) = ptr_ret_val;
  }

/****************************************************
 *  FILESYSTEM / DIRS                             ***
 ***************************************************/
  /* 0x20   ->  findfile()        [find_file()] */
  if(r->ax == 0x20) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((DirectoryEntry *)va_arg(arg, DirectoryEntry *)));
    i1 = (int)va_arg(arg, int);

    i_ret_val = find_file((DirectoryEntry far *)fp1, i1);

    arg = (void far *)(r_ebx - 2*sizeof(int));
    (int)(va_arg(arg, int)) = i_ret_val;
  }

  /* 0x21   ->  chdir()           [change_dir()] */
  if(r->ax == 0x21) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((char *)va_arg(arg, char *)));

    i_ret_val = change_dir((char far *)fp1);

    arg = (void far *)(r_ebx - 2*sizeof(int));
    (int)(va_arg(arg, int)) = i_ret_val;
  }

  /* 0x22   -> getcwd()           [get_current_working_dir()] */
  if(r->ax == 0x22) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((char *)va_arg(arg, char *)));

    ptr_ret_val = (void *)get_current_working_dir((char far *)fp1);

    arg=(void far *)(r_ebx - 2*sizeof(void *));
    (void *)(va_arg(arg, void *)) = ptr_ret_val;
  }

  /* 0x23   -> findfirst()        [findfirst()] */
  if(r->ax == 0x23) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((char *)va_arg(arg, char *)));
    fp2 = (void far *)(_api_lsegment + (unsigned int)((struct ffblk *)va_arg(arg, struct ffblk *)));
    i1  = (int)va_arg(arg, int);

    i_ret_val = findfirst((const char far *)fp1, (struct ffblk far *)fp2, i1);

    arg = (void far *)(r_ebx - 2*sizeof(int));
    (int)(va_arg(arg, int)) = i_ret_val;
  }

  /* 0x24   -> findnext()         [findnext()] */
  if(r->ax == 0x24) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((struct ffblk *)va_arg(arg, struct ffblk *)));

    i_ret_val = findnext((struct ffblk far *)fp1);

    arg = (void far *)(r_ebx - 2*sizeof(int));
    (int)(va_arg(arg, int)) = i_ret_val;
  }

  /* 0x25   -> findclose()        [findclose()] */
  if(r->ax == 0x25) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((struct ffblk *)va_arg(arg, struct ffblk *)));
    findclose((struct ffblk far *)fp1);
  }


/****************************************************
 *  FILESYSTEM / FILES                            ***
 ***************************************************/
  /* 0x30   ->  fnew()            [file_new()] */
  if(r->ax == 0x30) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((char *)va_arg(arg, char *)));

    i_ret_val = file_new((char far *)fp1);

    arg = (void far *)(r_ebx - 2*sizeof(int));
    (int)(va_arg(arg, int)) = i_ret_val;
  }

  /* 0x31   ->  fopen()           [_fopen() / file_open()] */
  if(r->ax == 0x31) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((char *)va_arg(arg, char *)));
    fp2 = (void far *)(_api_lsegment + (unsigned int)((char *)va_arg(arg, char *)));

    ptr_ret_val = (void *)_fopen((char far *)fp1, (char far *)fp2);

    arg = (void far *)(r_ebx - 2*sizeof(FILE *));
    (FILE *)(va_arg(arg, FILE *)) = (FILE *)ptr_ret_val;
  }

  /* 0x32   ->  fdopen()          [file_dir_open()] */
  if(r->ax == 0x32) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((char *)va_arg(arg, char *)));

    ptr_ret_val = (void *)file_dir_open((char far *)fp1);

    arg = (void far *)(r_ebx - 2*sizeof(FILE *));
    (FILE *)(va_arg(arg, FILE *)) = (FILE *)ptr_ret_val;
  }

  /* 0x33   ->  fseek()           [_fseek() / file_seek()] */
  if(r->ax == 0x33) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((FILE *)va_arg(arg, FILE *)));
    i1  = (int)va_arg(arg, int);
    i2  = (int)va_arg(arg, int);

    i_ret_val = _fseek((FILE far *)fp1, i1, i2);

    arg = (void far *)(r_ebx - 2*sizeof(int));
    (int)(va_arg(arg, int)) = i_ret_val;
  }

  /* 0x34   ->  fread()           [_fread() / file_read()] */
  if(r->ax == 0x34) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((char *)va_arg(arg, char *)));
    i1  = (int)va_arg(arg, int);
    i2  = (int)va_arg(arg, int);
    fp2 = (void far *)(_api_lsegment + (unsigned int)((FILE *)va_arg(arg, FILE *)));

    ptr_ret_val = (void *)_fread((char far *)fp1, i1, i2, (FILE far *)fp2);

    arg = (void far *)(r_ebx - 2*sizeof(void *));
    (void *)(va_arg(arg, void *)) = (void *)ptr_ret_val;
  }

  /* 0x35   ->  fwrite()          [_fwrite() / file_write()] */
  if(r->ax == 0x35) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((char *)va_arg(arg, char *)));
    i1  = (int)va_arg(arg, int);
    i2  = (int)va_arg(arg, int);
    fp2 = (void far *)(_api_lsegment + (unsigned int)((FILE *)va_arg(arg, FILE *)));

    _fwrite((char far *)fp1, i1, i2, (FILE far *)fp2);
  }

  /* 0x36   ->  fputc()           [_fputc()] */
  if(r->ax == 0x36) {
    ch1 = (char)va_arg(arg, char);
    fp1 = (void far *)(_api_lsegment + (unsigned int)((FILE *)va_arg(arg, FILE *)));

    _fputc(ch1, (FILE far *)fp1);
  }

  /* 0x37   ->  fputs()           [_fputs()] */
  if(r->ax == 0x37) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((char *)va_arg(arg, char *)));
    fp2 = (void far *)(_api_lsegment + (unsigned int)((FILE *)va_arg(arg, FILE *)));

    _fputs((char far *)fp1, (FILE far *)fp2);
  }

  /* 0x38   ->  fgetc()           [_fgetc()] */
  if(r->ax == 0x38) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((FILE *)va_arg(arg, FILE *)));

    c_ret_val = _fgetc((FILE far *)fp1);

    arg = (void far *)(r_ebx - 2*sizeof(char));
    (char)(va_arg(arg, char)) = (char)c_ret_val;
  }

  /* 0x39   ->  fgets()           [_fgets()] */
  if(r->ax == 0x39) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((char *)va_arg(arg, char *)));
    i1  = (int)va_arg(arg, int);
    fp2 = (void far *)(_api_lsegment + (unsigned int)((FILE *)va_arg(arg, FILE *)));

    ptr_ret_val = (void *)_fgets(fp1, i1, (FILE far *)fp2);

    arg = (void far *)(r_ebx - 2*sizeof(char *));
    (char *)(va_arg(arg, char *)) = (char *)ptr_ret_val;
  }

  /* 0x3A   ->  fclose()          [_fclose() / file_close()] */
  if(r->ax == 0x3A) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((char *)va_arg(arg, char *)));

    _fclose((FILE far *)fp1);
  }

  /* 0x3B   ->  mkdir()           [create_dir()] */
  if(r->ax == 0x3B) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((char *)va_arg(arg, char *)));

    i_ret_val = create_dir((char far *)fp1);

    arg = (void far *)(r_ebx - 2*sizeof(int));
    (int)(va_arg(arg, int)) = i_ret_val;
  }

  /* 0x3C   ->  rename()          [file_rename()] */
  if(r->ax == 0x3C) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((char *)va_arg(arg, char *)));
    fp2 = (void far *)(_api_lsegment + (unsigned int)((char *)va_arg(arg, char *)));

    i_ret_val = file_rename((char far *)fp1, (char far *)fp2);

    arg = (void far *)(r_ebx - 2*sizeof(int));
    (int)(va_arg(arg, int)) = i_ret_val;
  }

  /* 0x3D   ->  remove()          [file_remove()] */
  if(r->ax == 0x3D) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((char *)va_arg(arg, char *)));

    i_ret_val = file_remove((char far *)fp1);

    arg = (void far *)(r_ebx - 2*sizeof(int));
    (int)(va_arg(arg, int)) = i_ret_val;
  }

/****************************************************
 *  APM                                           ***
 ***************************************************/
  /* 0x40   -> APM_Installed()    [APM_Installed()]  */
  if(r->ax == 0x40) {
    arg = (void far *)(r_ebx - 2*sizeof(int));
    (int)(va_arg(arg, int)) = (int)APM_Installed();
  }

  /* 0x41   -> APM_PowerOff()     [APM_PowerOff()] */
  if(r->ax == 0x41) {
    arg = (void far *)(r_ebx - 2*sizeof(int));
    (int)(va_arg(arg, int)) = (int)APM_PowerOff();
  }

/****************************************************
 *  DATE&TIME                                     ***
 ***************************************************/
  /* 0x50   -> gettime()          [time_get()] */
  if(r->ax == 0x50) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((void *)va_arg(arg, void *)));
    time_get((struct Time far *)fp1);
  }

  /* 0x51   -> settime()          [time_set()] */
  if(r->ax == 0x51) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((void *)va_arg(arg, void *)));
    time_set((struct Time far *)fp1);
  }

  /* 0x52   -> getdate()          [date_get()] */
  if(r->ax == 0x52) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((void *)va_arg(arg, void *)));
    date_get((struct Date far *)fp1);
  }

  /* 0x53   -> settime()          [date_set()] */
  if(r->ax == 0x53) {
    fp1 = (void far *)(_api_lsegment + (unsigned int)((void *)va_arg(arg, void *)));
    date_set((struct Date far *)fp1);
  }

/****************************************************
 *  EXECUTING                                     ***
 ***************************************************/
  /* 0x60   -> exec_first_free_segment() */
  if(r->ax == 0x60) {
    i1 = (unsigned int)exec_first_free_segment();
    arg = (void far *)(r_ebx - 2*sizeof(void *));
    (unsigned int)(va_arg(arg, unsigned int)) = (unsigned int)i1;
  }

  /* 0x61   -> exec_set_segment_status() */
  if(r->ax == 0x61) {
    i1  = (int)va_arg(arg, int);
    b1  = (bool)va_arg(arg, bool);

    exec_set_segment_status((unsigned int)i1, b1);
  }

  /* 0x62   -> exec_load_app() */
  if(r->ax == 0x62) {
    i1  = (unsigned int)va_arg(arg, int);
    i2  = (unsigned int)va_arg(arg, int);
    i3  = (unsigned int)va_arg(arg, int);

    exec_load_app((unsigned int)i1, (unsigned int)i2, (unsigned int)i3);
  }

/****************************************************
 *  SYSTEMOVE FUNKCE                              ***
 ***************************************************/
  /* 0xA0   -> vrati adresu funkce main() */
  if(r->ax == 0xA0) {
    arg = (void far *)(r_ebx - 2*sizeof(unsigned int));
    (unsigned int)(va_arg(arg, unsigned int)) = (unsigned int)main;
  }

  /* 0xA1   -> vrati cislo - kolikrat se jiz zavolaly API */
  if(r->ax == 0xA1) {
    /* pro tuto API vsak counter odecteme */
    --_api_counter;
    arg = (void far *)(r_ebx - 2*sizeof(unsigned int));
    (unsigned long long)(va_arg(arg, unsigned long long)) = (unsigned long long)_api_counter;
  }

  return;
}

/**
 *  _int()
 *  po zavolani 'int OSINT' je tato funkce prvni, ktera se provede
 */
void interrupt _int() {
static _regs r;

  asm cli                 ;

  /* zazalohujeme si dulezite registry */
  asm push ds             ;
  asm push es             ;
  asm push si             ;
  asm push di             ;

  /* ulozime si SP a SS do registru SI a CX */
  asm mov si, sp          ;
  asm mov cx, ss          ;

  /* nastavime segmentove registry na segment jadra */
  asm mov dx, cs          ;
  asm mov ds, dx          ;
  asm mov es, dx          ;
  asm mov ss, dx          ;

  /* nyni prisla chvile pushnout si SP a SS a tim si je zalohovat */
  asm push si             ;
  asm push cx             ;

  /* nastavime registr AX a BX na projeti API Handlerem */
  asm mov r.ax, ax        ;
  asm mov r.bx, bx        ;
  asm sti                 ;

  /* v CX je puvodni SS, tedy segment volajici aplikace - zalohujeme si ho */
  r.segment    = (long)((long)((long)(_CX & 0xFFFF) << 16) & 0xF0000000);
  _api_segment = _CX;

  /* podle registru (AX a BX) se provede pozadovana API fce */
  APIHandler(&r)          ;

  asm cli                 ;

  /* nyni vratime zpet SP a SS */
  asm pop cx              ;
  asm pop si              ;
  asm mov ss, cx          ;
  asm mov sp, si          ;

  /* a DI, SI, ES a DS */
  asm pop di              ;
  asm pop si              ;
  asm pop es              ;
  asm pop ds              ;
  asm sti                 ;

  /* a hura z5 do programu =) */
  return;
}
